#include <stdint.h>
#include <string>
#include <stdio.h>

#include <ace/xcomponent/native_interface_xcomponent.h>

#include "../plugin_manager.h"
#include "napi/common/OhosLog.h"

#include <js_native_api_types.h>
#include "RawFileUtils.h"


NativeResourceManager* RawFileUtils::nativeResourceManager_ = nullptr;

bool RawFileUtils::InitResourceManager(napi_env env, napi_value param) {
    nativeResourceManager_ = OH_ResourceManager_InitNativeResourceManager(env, param);
    OHOS_LOGD("cocos qgh  initResourceManager %{public}p", nativeResourceManager_);
    return true;
}

std::vector<std::string> RawFileUtils::searchFiles(const char *folder, bool recursive) {
    std::vector<std::string> results;
    char *realFolder = const_cast<char*> (folder);
    if (strcmp(folder, "/") == 0) {
        realFolder = "";
    }
    auto dir = OH_ResourceManager_OpenRawDir(nativeResourceManager_, realFolder);
    if (dir) {
        int file_count = GetDirSize(dir);
        for (int index = 0; index < file_count; index++) {
            std::string fileName = OH_ResourceManager_GetRawFileName(dir, index);
            auto item = std::string(realFolder) + "/" + fileName;
            results.push_back(item);
            if (recursive) {
                auto items = searchFiles(item.c_str(), recursive);
                results.insert(results.end(), items.begin(), items.end());
            }
        }
        OH_ResourceManager_CloseRawDir(dir);
    }
    return results;
}


std::string RawFileUtils::getWritableFilePath(){
    return "/data/storage/el2/base/haps/entry/files/rawfileResources/";
}

/**
 * create subDir in RawFileResourcePath
 * @param path
 * @return
 */
bool RawFileUtils::createDirectory(const std::string &subDir){
    // Split the path
    size_t start = 0;
    size_t found = subDir.find_first_of("/\\", start);
    std::string subpath;
    std::vector<std::string> dirs;

    if (found != std::string::npos) {
        while (true) {
            subpath = subDir.substr(start, found - start + 1);
            if (!subpath.empty())
                dirs.push_back(subpath);
            start = found + 1;
            found = subDir.find_first_of("/\\", start);
            if (found == std::string::npos) {
                if (start < subDir.length()) {
                    dirs.push_back(subDir.substr(start));
                }
                break;
            }
        }
    }

    DIR *dir = NULL;

    subpath = "";
    for (const auto &iter : dirs) {
        subpath += iter;
        dir = opendir(subpath.c_str());

        if (!dir) {
            int ret = mkdir(subpath.c_str(), S_IRWXU | S_IRWXG | S_IRWXO);
            if (ret != 0 && (errno != EEXIST)) {
                return false;
            }
        } else {
            closedir(dir);
        }
    }
    return true;
}

bool RawFileUtils::fileIsExistInRawPath(const char *filename) {
    RawFile64 *rawFile = Open(filename);
    if (rawFile) {
        Close(rawFile);
        return true;
    }
    return false;
}

bool RawFileUtils::fileIsExistInWriteablePath(const char *filename) {
    return fileIsExistInSpecifyPath(filename,getWritableFilePath().c_str());
}

const char *RawFileUtils::getFilePathInWriteablePath(const char *filename) {
    std::string absolutePath = getWritableFilePath();
    std::string fileNameStr = std::string(filename);
    absolutePath.append(fileNameStr);
    return absolutePath.c_str();
}

const char *RawFileUtils::copyRawFile2WriteablePath(const char *filename) {
    RawFile64 *rawFile = Open(filename);
    if (rawFile == NULL) {
        return NULL;
    }
    std::string copyFileName = std::string(filename);
    // set result equal writeable app path
    std::string absolutePath = getWritableFilePath();
    auto path = copyRawFile2SpecifyPath(rawFile, absolutePath, copyFileName);
    Close(rawFile);

    return path.c_str();
}


bool RawFileUtils::fileIsExistInSpecifyPath(const char *filename, const char *specifyPath) {
    std::string filePath = std::string(specifyPath);
    filePath.append(filename);

    FILE *file = fopen(filePath.c_str(), "r");
    bool fileExists = false;
    if (file) {
        fileExists = true;
        fclose(file);
    }
    return fileExists;
}


const char *RawFileUtils::copyRawFile2SpecifyPath(const char *filename, const char *path) {
    RawFile64 *rawFile = Open(filename);
    if (rawFile == NULL) {
        return NULL;
    }

    std::string copyFileName = std::string(filename);
    std::string specifyPath = std::string(path);
    std::string writeableFilePath =copyRawFile2SpecifyPath(rawFile, specifyPath, copyFileName);
    Close(rawFile);

    return writeableFilePath.c_str();
}

/**
 * copy file to specify path
 * @param RawFile
 * @param path
 * @param fileName
 */
std::string RawFileUtils::copyRawFile2SpecifyPath(RawFile64 *rawFile, std::string &path, std::string fileName){
    if (!rawFile) {
        return "";
    }

    long length = this->GetSize(rawFile);
    std::unique_ptr<uint8_t[]> data = std::make_unique<uint8_t[]>(length);
    this->Read(rawFile, data.get(), length);
    std::string filePathAndName = path + fileName;

    std::string dirPath = filePathAndName;
    size_t p = dirPath.find_last_of('/', dirPath.length());
    if (p != -1) {
        createDirectory(dirPath.substr(0, p));
    }
    FILE *fp = fopen(filePathAndName.c_str(), "wb");
    if (!fp)
        return "";
    fwrite(data.get(), length, 1, fp);
    fclose(fp);
    data.reset();
    return filePathAndName;
}